﻿using Evergine.Bindings.RenderDoc;
using OpenTK.Mathematics;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using System;
using System.Collections.Generic;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using TextCopy;

namespace ttbit.Windows
{
    class GLPlatWin : IPlatform
    {
        //打开一个DebugMode




        GameWindow? win = null;
        IApp app;
        RenderDoc? renderdoc;
        string _assetpath;
        string _rootpath;

        static int lastX = 0;
        static int lastY = 0;
        static int downmx = 0;
        static int downmy = 0;
        static int downsx = 0;
        static int downsy = 0;
        static bool mousedrag = false;
        static bool resizeBottom = false;
        //static bool resizeLeft = false;
        static bool resizeRight = false;
        void RenderDoc_Init()
        {
            var b = RenderDoc.Load(out renderdoc);
            if (b)
            {
                uint mask = (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_None;
                renderdoc.API.MaskOverlayBits(0, mask);

                unsafe
                {
                    RENDERDOC_InputButton[] keys = new RENDERDOC_InputButton[] { RENDERDOC_InputButton.eRENDERDOC_Key_F11 };
                    fixed (RENDERDOC_InputButton* pkey = keys)
                    {
                        renderdoc.API.SetCaptureKeys(pkey, 0);//不要抓帧快捷键
                        renderdoc.API.SetFocusToggleKeys(null, 0);
                    }
                }
                RenderDoc_Overlay(true, true, false);
            }
            else
            {
                renderdoc = null;
            }
        }

        void RenderDoc_Overlay(bool framerate, bool framenumber, bool caplist)
        {
            if (renderdoc != null)
            {
                uint mask = (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_None;
                if (framerate)
                    mask |= (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_Enabled | (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_FrameRate;
                if (framenumber)
                    mask |= (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_Enabled | (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_FrameNumber;
                if (caplist)
                    mask |= (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_Enabled | (uint)RENDERDOC_OverlayBits.eRENDERDOC_Overlay_CaptureList;

                renderdoc.API.MaskOverlayBits(0, mask);
            }
        }
        public GLPlatWin(IApp app)
        {
            pixelSize = 1.0f;//给默认值
            RenderDoc_Init();
            this.app = app;

            var _path = Path.GetDirectoryName(GetType().Assembly.Location);
            if (_path == null)
                throw new Exception("error path.");
            _assetpath = _path;

            GameWindowSettings set = new GameWindowSettings();
            NativeWindowSettings setn = new NativeWindowSettings();

            //在windows 和 linux 上均可以直接使用GLES
            {
                setn.API = ContextAPI.OpenGLES;
                setn.APIVersion = new Version(3, 0);
            }

            bool useAngle = false;
            if (
                RuntimeInformation.IsOSPlatform(OSPlatform.OSX)
                )//Macos 已经放弃了GL，所以我们需要Angel来接手，模拟一个gles3给我们
            {
                useAngle = true;
                if (RuntimeInformation.OSArchitecture == Architecture.X64)
                {
                    File.Delete(Path.Combine(_assetpath, "libEGL.dylib"));
                    File.Delete(Path.Combine(_assetpath, "libGLESv2.dylib"));
                    File.Copy(Path.Combine(_assetpath, "macos_x64", "libEGL.dylib"), Path.Combine(_assetpath, "libEGL.dylib"));
                    File.Copy(Path.Combine(_assetpath, "macos_x64", "libGLESv2.dylib"), Path.Combine(_assetpath, "libGLESv2.dylib"));
                }
                else if (RuntimeInformation.OSArchitecture == Architecture.X86)
                {
                    throw new Exception("还沒有編譯x86 版本的google angle for macos，你可以自己编译一份");
                }
                else if (RuntimeInformation.OSArchitecture == Architecture.Arm64)
                {
                    throw new Exception("沒有編譯Arm64 版本的google angle for macos，你可以自己编译一份");
                }
                else
                {
                    throw new Exception("not support arch:" + RuntimeInformation.OSArchitecture);
                }
            }


            if (useAngle)
            {//这些代码主要是为了让glfw 强制走egl路径，这样就可以通过替换egl文件让angel接手
                int GLFW_CONTEXT_CREATION_API = 0x0002200B;
                int GLFW_EGL_CONTEXT_API = 0x00036002;
                //glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
                GLFW.WindowHint((WindowHintInt)GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
            }
            setn.Title = "Lying Flat Engine.";
            setn.StartVisible = false;

            win = new GameWindow(set, setn);
            win.IsEventDriven = false;
            win.WindowBorder = WindowBorder.Hidden;
            win.VSync = VSyncMode.On;
            win.Load += OnLoad;
            win.Unload += OnUnload;
            win.UpdateFrame += OnUpdateFrame;
            win.RenderFrame += OnRenderFrame;
            win.Resize += OnResize;
            win.Refresh += OnRefresh;
            win.TextInput += OnTextInput;
            win.KeyDown += OnKeyDown;
            win.KeyUp += OnKeyUp;

            RefreshScreenSize();
        }
        public void Run()
        {
            win.Run();
        }

        public string Name => "Windows";

        //外部资源路径，可以直接用File.IO方法操作
        public string UserDataPath
        {
            get
            {
                return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "ttengine");
            }
        }
        //内部资源路径，可以直接用File.IO方法操作
        //内部资源路径用来放置框架内部资源
        //与 OpenCoreAsset 不同，内部资源是分离的，OpenAsset 是Core内置的资源
        public string ResourcePath
        {
            get
            {
                //var tmppath = System.IO.Path.Combine(_assetpath, "temp");
                //if (System.IO.Directory.Exists(tmppath) == false)
                //    System.IO.Directory.CreateDirectory(tmppath);
                return _rootpath;
            }
        }



        public void RefreshScreenSize()
        {
            unsafe
            {
                int x, y, w, h;
                OpenTK.Windowing.GraphicsLibraryFramework.Monitor* monptr = GLFW.GetWindowMonitor(win.WindowPtr);
                if (monptr == null)
                {
                    OpenTK.Windowing.GraphicsLibraryFramework.Monitor** monitorsRaw = GLFW.GetMonitorsRaw(out int count);
                    monptr = monitorsRaw[0];
                }
                if (monptr == null)
                {
                    throw new Exception("can not find monitor by anyway.");
                }

                GLFW.GetMonitorWorkarea(monptr, &x, &y, &w, &h);
                GLFW.GetMonitorContentScale(monptr, out float sx, out float sy);
                ScreenWidth = w;
                ScreenHeight = h;
                ScreenScaleH = sy;
                ScreenScaleV = sy;

                //这个方法在macos 会出错，重新实现一下看看
                //var mon = OpenTK.Windowing.Desktop.Monitors.GetMonitorFromWindow(win.WindowPtr);
                //ScreenWidth = mon.HorizontalResolution;
                //ScreenHeight = mon.VerticalResolution;
                //ScreenScaleH = mon.HorizontalScale;
                //ScreenScaleV = mon.VerticalScale;
            }
        }
        public bool CanResize => true;

        public bool CanToogleFullScreen => true;

        public int ScreenWidth
        {
            get;
            private set;
        }
        public int ClientWidth
        {
            get
            {
                return win.ClientSize.X;
            }
        }
        public int ClientHeight
        {
            get
            {
                return win.ClientSize.Y;
            }

        }

        public int ScreenHeight
        {
            get;
            private set;
        }

        public float ScreenScaleH
        {
            get;
            private set;
        }

        public float ScreenScaleV
        {
            get;
            private set;
        }

        public float pixelSize { get; set; }

        public bool IsFrameDebugInit => renderdoc != null;

        public bool CanHide => true;

        public void GL_Display()
        {
            win.SwapBuffers();
        }

        //ITTGL _glapi = new GLWin();
        //public ITTGL GL_GetAPI()
        //{

        //    return _glapi;
        //}
        //IAudio _alapi = new ALWin();
        //public IAudio AL_GetAPI()
        //{
        //    return _alapi;
        //}


        public Stream CoreAssetOpen(string path)
        {
            var file = Path.Combine(_assetpath, path);
            return File.OpenRead(path);
        }

        public void ResizeAndCenter(int w, int h)
        {
            RefreshScreenSize();

            int width = w;
            int height = h;
            var scaleh = pixelSize * ScreenScaleH;
            var scalew = pixelSize * ScreenScaleV;
            int newwidth = (int)(width * scalew);
            int newheight = (int)(height * scaleh);
            win.Size = new Vector2i(newwidth, newheight);

            //win.CenterWindow();这个方法在macos也会报错，傻逼苹果

            win.Location = new Vector2i((ScreenWidth - newwidth) / 2, (ScreenHeight - newheight) / 2);
        }
        public void StartMouseDrag()
        {
            //拖动左上角在变，用相对模式好
            mousedrag = true;
        }
        public void StartMouseScale(bool right, bool bottom)
        {
            //缩放右下角时，因为窗口坐标不会改变，可以用绝对模式
            resizeBottom = bottom;
            resizeRight = right;
            downmx = MouseX;
            downmy = MouseY;
            downsx = win.ClientSize.X;
            downsy = win.ClientSize.Y;
        }
        public void SetPos(int x, int y, int w, int h)
        {
            win.ClientRectangle = new Box2i(x, y, x + w, y + h);
        }



        ///////////////////////////////////////////////////////////
        void OnLoad()
        {
            app.OnInit(this, win.Location.X, win.Location.Y, win.Size.X, win.Size.Y);
        }
        void OnUnload()
        {

            app.OnExit();
        }
        bool MousePress0;
        int MouseX;
        int MouseY;
        bool lastMousePress0 = false;
        int lastMouseX = -1;
        int lastMouseY = -1;
        void UpdateDrag()
        {
            int x = MouseX;
            int y = MouseY;
            int wx = x + win.ClientRectangle.Min.X;
            int wy = y + win.ClientRectangle.Min.Y;

            if (!MousePress0)
            {
                mousedrag = false;
                resizeBottom = false;
                resizeRight = false;
            }


            if (mousedrag)
            {
                var mx = wx - lastX;
                var my = wy - lastY;
                win.Location = new Vector2i(win.ClientRectangle.Min.X + mx, win.ClientRectangle.Min.Y + my);
            }
            else if (resizeBottom || resizeRight)//|| resizeLeft)
            {
                //因为限制尺寸的存在，用绝对定位鼠标更稳定
                int mx = 0;
                int my = 0;

                var newx = win.Size.X;
                var newy = win.Size.Y;
                if (resizeBottom)
                {
                    my = wy - lastY;


                    newy = MouseY - downmy + downsy;
                }
                if (resizeRight)
                {
                    mx = wx - lastX;

                    newx = MouseX - downmx + downsx;
                }
                //绝对
                var width = newx;
                var height = newy;
                //相对
                //var width = win.Size.X + mx;
                //var height = win.Size.Y + my;

                if (width < 512)
                    width = 512;
                if (height < 256)
                    height = 256;
                var scaleh = pixelSize * ScreenScaleH;
                var scalew = pixelSize * ScreenScaleV;
                int newwidth = (int)((int)(width / scalew) * scalew);
                int newheight = (int)((int)(height / scaleh) * scaleh);

                win.Size = new Vector2i(width, height);
            }

            lastX = wx;
            lastY = wy;
        }

        void OnUpdateFrame(FrameEventArgs t)
        {
            MousePress0 = win.MouseState.IsButtonDown(MouseButton.Button1);
            MouseX = (int)win.MouseState.X;
            MouseY = (int)win.MouseState.Y;

            UpdateDrag();

            if (MousePress0 == lastMousePress0 && lastMouseX == MouseX && lastMouseY == MouseY)
            {
                //无变化别发pointevent
            }
            else
            {
                app.OnPointEvent(0, MouseX, MouseY, MousePress0);
                lastMousePress0 = MousePress0;
                lastMouseX = MouseX;
                lastMouseY = MouseY;
            }
            app.OnUpdate(t.Time);
        }
        void OnRenderFrame(FrameEventArgs t)
        {
            app.OnRender();
            win.SwapBuffers();
        }
        void OnResize(ResizeEventArgs args)
        {
            app.OnResize(win.Location.X, win.Location.Y, args.Width, args.Height);
        }
        void OnRefresh()
        {

        }
        void OnTextInput(TextInputEventArgs arg)
        {
            app.OnInputText(arg.AsString);
        }
        void OnKeyDown(KeyboardKeyEventArgs arg)
        {
            app.OnKeyDown((Keys)arg.Key, (KeyModifiers)arg.Modifiers);
        }
        void OnKeyUp(KeyboardKeyEventArgs arg)
        {
            app.OnKeyUp((Keys)arg.Key, (KeyModifiers)arg.Modifiers);
        }

        public void FrameDebug_Overlay(bool show)
        {
            RenderDoc_Overlay(show, show, false);
        }

        public void FrameDebug_OpenApp()
        {
            if (renderdoc != null)
            {
                if (renderdoc.API.IsTargetControlConnected() > 0)
                {
                    renderdoc.API.ShowReplayUI();
                }
                else
                {
                    renderdoc.API.LaunchReplayUI(1, "");
                }
            }
        }

        public void FrameDebug_StartFrameCapture()
        {
            if (renderdoc != null)
            {
                renderdoc.API.StartFrameCapture(IntPtr.Zero, IntPtr.Zero);
            }
        }

        public void FrameDebug_EndFrameCapture()
        {
            if (renderdoc != null)
            {
                renderdoc.API.EndFrameCapture(IntPtr.Zero, IntPtr.Zero);
            }
        }

#if windows
        [DllImport("user32.dll", EntryPoint = "GetTopWindow", CharSet = CharSet.Auto, SetLastError = true)]
        static extern IntPtr GetTopWindow(IntPtr hwnd);

        [DllImport("user32.dll")]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndlnsertAfter, int X, int Y, int cx, int cy, uint Flags);
        const uint SWP_NOSIZE = 1;// {忽略 cx、cy, 保持大小
        const uint SWP_NOMOVE = 2;// {忽略 X、Y, 不改变位置}
#endif
        Box2i lastwindowedpos = Box2i.Empty;
        bool _fullscreen = false;
        public bool FullScreen
        {
            get
            {
                return _fullscreen;
            }
            set
            {

                if (value == true)
                {
                    if (_fullscreen == false)
                    {
                        lastwindowedpos = win.ClientRectangle;
                    }
#if WINDOWS
                    //给窗口topmost属性，这样方便全屏窗口化
                    unsafe
                    {
                        var hwnd = GLFW.GetWin32Window(win.WindowPtr);
                        IntPtr ptr = (IntPtr)(-1);//TOPMOST
                        SetWindowPos(hwnd, ptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                    }
#endif
                    win.ClientRectangle = new Box2i(0, 0, ScreenWidth, ScreenHeight);
                    _fullscreen = true;
                }
                else
                {
#if WINDOWS
                    //给窗口topmost属性，这样方便全屏窗口化
                    unsafe
                    {
                        var hwnd = GLFW.GetWin32Window(win.WindowPtr);
                        IntPtr ptr = (IntPtr)(0);//TOP
                        SetWindowPos(hwnd, ptr, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
                    }
#endif
                    win.ClientRectangle = lastwindowedpos;
                    _fullscreen = false;
                }
            }
        }
        Process proccur = null;
        public uint MemoryUseByKB
        {
            get
            {
                if (proccur == null) proccur = Process.GetCurrentProcess();
                //Process proc = Process.GetCurrentProcess();
                return (uint)(proccur.PrivateMemorySize64 / 1024);
            }
        }
        public float BatteryLevel => 0;

        public BatteryState BatteryState => BatteryState.Unknown;//非移动平台没必要

        public bool Active => true;

        public void Hide()
        {
            win.IsVisible = false;
        }

        public void Show()
        {
            win.IsVisible = true;
        }

        public void Close()
        {
            win.Close();
        }

        public MemoryStream CoreAssetReadAll(string path)
        {
            var file = Path.Combine(_assetpath, path);
            var bts = File.ReadAllBytes(file);
            return new MemoryStream(bts);

        }

        public string CoreAssetReadAllText(string path)
        {
            var file = Path.Combine(_assetpath, path);
            var txt = File.ReadAllText(file);
            return txt;
        }

        public string? ClipBoard_GetText()
        {
            return ClipboardService.GetText();
        }

        public void ClipBoard_SetText(string txt)
        {
            ClipboardService.SetText(txt);
        }

        public bool ShowInputBox(Action<string> callback, string title, string message, string btnok = "OK", string btnCancel = "Cancel", string? placeholder = null, int maxLength = -1, string? initialValue = null)
        {
            return false;
        }

        public bool IsDesktop()
        {
            return true;
        }
    }
}
